#include "CudaPerson.h"
using namespace std;


void CopyFromCPUtoGPU(
	vector<Person>&  cpu_person, dPersonVec* gpu_person,
	hCPUtoGPUPersonVec* cpu_temp_person, dCPUtoGPUPersonVec* gpu_temp_person,int size){

	
	#pragma omp parallel for
	for(int i = 0; i < size; i++){
		(*cpu_temp_person)[i].CopyFromCPU(cpu_person[i]);
	}
	cudaMemcpy(thrust::raw_pointer_cast(gpu_temp_person->data()),&((*cpu_temp_person)[0]),
		size*sizeof(CPUtoGPUPerson),cudaMemcpyHostToDevice);
	thrust::for_each(gpu_person->begin(),gpu_person->end(),
		CPUtoGPUPersonOp(thrust::raw_pointer_cast(gpu_temp_person->data())));

	//cudaMemcpy(thrust::raw_pointer_cast(gpu_person->data()),&(cpu_person[0]),size*sizeof(Person),cudaMemcpyHostToDevice);
}

int comparePpl(Person& p1, Person& p2){
  int result = 0;
  if(p1.id != p2.id) result |= 1;	// agent id
  if(p1.age != p2.age) result |= 2;	// age group
  if(p1.status != p2.status) result |= (2 << 2);	// infection status
  if(p1.nWhichVload != p2.nWhichVload) result |= (2 << 3);    // which viral load trajectory (vload) to use?
  if(p1.iday != p2.iday) result |= (2 << 4);            // infected days
  if(p1.ibits != p2.ibits) result |= (2 << 5);  // state info for infection
  if(p1.vbits != p2.vbits) result |= (2 << 6);  // state info for vaccine
  if(p1.vday != p2.vday) result |= (2 << 7);   // vaccination timer (number of days elapsed)
  if(p1.nAVTimer != p2.nAVTimer) result |= (2 << 8);        // antiviral timer (number of tablets left)
  if(p1.nQuarantineTimer != p2.nQuarantineTimer) result |= (2 << 9); // quarantine timer (number of days left)
  if(p1.nTravelTimer != p2.nTravelTimer) result |= (2 << 10);	// travel time left
  if(p1.nDayNeighborhood != p2.nDayNeighborhood) result |= (2 << 11); // ID of work neighborhood

  if(p1.nDayTract != p2.nDayTract) result |= (2 << 12);        // ID of daytime (work, school) tract
  if(p1.nDayComm != p2.nDayComm) result |= (2 << 13);         // ID of daytime (work, school) community
  if(p1.nWorkplace != p2.nWorkplace) result |= (2 << 14);	// work or school group
  if(p1.fBaselineVES != p2.fBaselineVES) result |= (2 << 15); // baseline VES before vaccination
  if(p1.nHomeComm != p2.nHomeComm) result |= (2 << 16);        // ID of home community

  if(p1.family != p2.family) result |= (2 << 17);	        // family ID
  if(p1.nFamilySize != p2.nFamilySize) result |= (2 << 18);  // family size
  if(p1.nHomeNeighborhood != p2.nHomeNeighborhood) result |= (2 << 19);    // neighborhood (0-3)
  if(p1.householdcluster != p2.householdcluster) result |= (2 << 20);// household cluster
  if(p1.nVaccinePriority != p2.nVaccinePriority) result |= (2 << 21); // vaccine priority group (0 for no vaccine, 1 for highest priority, 2 for next-highest priority, etc). Also set to 0 if the person does not want vaccine.
  if(p1.nVaccineRestrictionBits != p2.nVaccineRestrictionBits) result |= (2 << 22); // for keeping track of vaccine restriction categories

  // temporary data
  if(p1.pri != p2.pri) result |= (2 << 23); // infectiousness multiplier
  if(p1.prs != p2.prs) result |= (2 << 24); // susceptibility multiplier

  return result;
}

void CopyFromGPUtoCPU(
	vector<Person>&  cpu_person, dPersonVec* gpu_person,
	hGPUtoCPUPersonVec* cpu_temp_person, dGPUtoCPUPersonVec* gpu_temp_person,int size){
	

	thrust::for_each(gpu_person->begin(),gpu_person->end(),
		GPUtoCPUPersonOp(thrust::raw_pointer_cast(gpu_temp_person->data())));
		
	cudaMemcpy(&((*cpu_temp_person)[0]),thrust::raw_pointer_cast(gpu_temp_person->data()),
		size*sizeof(GPUtoCPUPerson),cudaMemcpyDeviceToHost);
	
	#pragma omp parallel for
	for(int i = 0 ; i < size; i++){
		(*cpu_temp_person)[i].CopyToCPU(cpu_person[i]);	
	}
	
	//cudaMemcpy(&(cpu_person[0]),thrust::raw_pointer_cast(gpu_person->data()),size*sizeof(Person),cudaMemcpyDeviceToHost);
}